package com.bnq.face

import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.*
import android.hardware.Camera
import android.os.Build
import android.os.Bundle
import android.view.View
import android.view.Window
import android.view.WindowManager
import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageView
import android.widget.TextView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import com.bnq.face.CameraUtils.NextPreviewFrame
import kotlinx.coroutines.*
import java.io.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine


class FaceHandleActivity: Activity() ,View.OnClickListener{

    private val REQUEST_CAMERA = 0x01

    private var mCameraSurfaceView: CameraSurfaceView? = null
    private var mBtnTake: Button? = null
    private var mBtnSwitch: Button? = null

    private var mOrientation = 0

    // CameraSurfaceView 容器包装类
    private var mAspectLayout: FrameLayout? = null
    private var mCameraRequested = false

    private var mPreview: ImageView? = null
    private var mIvFaceStatus: ImageView? = null
    private var mIvLuminance: ImageView? = null
    private var mIvBlurry: ImageView? = null
    private var mType = 0
    private var mTvTips: TextView? = null
    private var mTvSubTips: TextView? = null

    private var mCoroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO)

    private var isCompress:Boolean = false
    private var mDistanceThresh:Double = 20.0
    private var mMiniThresh:Double = 100.0
    private var mMaxThresh:Double = 210.0
    private var mBlueThresh:Double = 80.0
    private var mIouNormal:Double = 0.8
    private var mIouInter:Double = 0.35
    private var mTLXRatio:Double = 0.25
    private var mTLYRatio:Double = 0.25
    private var mRBXRatio:Double = 0.75
    private var mRBYRatio:Double = 0.65
    private var mCanFlip:Boolean = true


    override fun onCreate(savedInstanceState: Bundle?) {
        if (Build.VERSION.SDK_INT == 26 && ActivityUtils.isTranslucentOrFloating(this)) {
            ActivityUtils.fixOrientation(this)
        }
        super.onCreate(savedInstanceState)
        requestWindowFeature(Window.FEATURE_NO_TITLE)
        window.setFlags(
            WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
        )
        setContentView(R.layout.activity_face_handle)
        mType = intent?.getIntExtra("return_type",0)?:0
        isCompress = intent?.getBooleanExtra("is_compress",false)?:false
        // Android 6.0相机动态权限检查
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            == PackageManager.PERMISSION_GRANTED
        ) {
            initView()
        } else {
            ActivityCompat.requestPermissions(
                this, arrayOf(
                    Manifest.permission.CAMERA,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
                ), REQUEST_CAMERA
            )
        }
        BrightnessHelper(this).setMaxBrightness()
    }

    private fun getDoubleExtra(name: String): Double? {
        return ((intent?.getDoubleExtra(name,-1.0)?.let { if(it < 0) null else it })
            ?:(intent?.getFloatExtra(name,-1.0f)?.let { if(it < 0) null else it.toDouble() })
            ?:(intent?.getIntExtra(name,-1)?.let { if(it < 0) null else it.toDouble() }))
    }

    /**
     * 初始化View
     */
    private fun initView() {
        mAspectLayout = findViewById<View>(R.id.layout_aspect) as FrameLayout
        mCameraSurfaceView = findViewById(R.id.surface_view)//CameraSurfaceView(this)
        //mAspectLayout!!.addView(mCameraSurfaceView)
        mOrientation = CameraUtils.calculateCameraPreviewOrientation(this)
        mBtnTake = findViewById<View>(R.id.btn_take) as Button
        mBtnTake!!.setOnClickListener(this)
        mBtnSwitch = findViewById<View>(R.id.btn_switch) as Button
        mBtnSwitch!!.setOnClickListener(this)
        mPreview = findViewById<ImageView>(R.id.preview)
        val switch_camera = findViewById<View>(R.id.switch_camera)
        switch_camera.setOnClickListener(this)
        findViewById<View>(R.id.close).setOnClickListener(this)
        mIvFaceStatus = findViewById(R.id.iv_face_status)
        mIvLuminance = findViewById(R.id.iv_luminance)
        mIvBlurry = findViewById(R.id.iv_blurry)
        mTvTips = findViewById<TextView>(R.id.tips)
        mTvSubTips = findViewById<TextView>(R.id.sub_tips)

        //设置文案
        val tips = intent?.getStringExtra("tips")
        if(!tips.isNullOrEmpty()){
            mTvTips?.text = tips
        }
        val subTips = intent?.getStringExtra("sub_tips")
        if(subTips.isNullOrEmpty()) {
            mTvSubTips?.visibility = View.GONE
        }else{
            mTvSubTips?.visibility = View.VISIBLE
            mTvSubTips?.text = subTips
        }

        getDoubleExtra("distance_thresh")?.let {
            mDistanceThresh = it
        }
        getDoubleExtra("mini_thresh")?.let {
            mMiniThresh = it
        }
        getDoubleExtra("max_thresh")?.let {
            mMaxThresh = it
        }
        getDoubleExtra("blue_thresh")?.let {
            mBlueThresh = it
        }
        getDoubleExtra("iou_normal")?.let {
            mIouNormal = it
        }
        getDoubleExtra("iou_inter")?.let {
            mIouInter = it
        }

        getDoubleExtra("tlx_ratio")?.let {
            mTLXRatio = it
        }
        getDoubleExtra("tly_ratio")?.let {
            mTLYRatio = it
        }
        getDoubleExtra("rbx_ratio")?.let {
            mRBXRatio = it
        }
        getDoubleExtra("rby_ratio")?.let {
            mRBYRatio = it
        }
        mCanFlip = intent?.getBooleanExtra("can_flip",true)?:true
        switch_camera.visibility = if(mCanFlip) View.VISIBLE else View.INVISIBLE

        //println("-----------mDistanceThresh:$mDistanceThresh,mMiniThresh:$mMiniThresh,mMaxThresh:$mMaxThresh,mBlueThresh:$mBlueThresh")

        /*CameraUtils.setPreviewCallback { bytes, camera ->
            *//*val yuvimage =
                YuvImage(bytes, ImageFormat.NV21, CameraUtils.DEFAULT_WIDTH,CameraUtils.DEFAULT_HEIGHT, null)
            val bos = ByteArrayOutputStream()
            yuvimage.compressToJpeg(Rect(0, 0, CameraUtils.DEFAULT_WIDTH,CameraUtils.DEFAULT_HEIGHT), 100, bos)
            val jdata: ByteArray = bos.toByteArray()
            val bitmap = BitmapFactory.decodeByteArray(jdata, 0, jdata.size).let {
                Bitmap.createBitmap(it,0,0,it.width,it.height,
                    Matrix().apply {
                        if(CameraUtils.getCameraID() == Camera.CameraInfo.CAMERA_FACING_FRONT){
                            postRotate(-90f)
                            postScale(-1f,1f)
                        }else{
                            postRotate(90f)
                        }
                        },

                    true)
            }
            mPreview?.setImageBitmap(bitmap)*//*
            Log.e("Face","----------------setPreviewCallbackWithBuffer:${bytes.size},${Thread.currentThread().name}")
        }*/

        //showImage()
        faceDetect()
    }

    private fun faceDetect(){
        mCoroutineScope.launch {
            innerFaceDetect()
        }
    }

    private fun updateFaceDetectToView(faceResult: FaceResult){
        mCoroutineScope.launch(Dispatchers.Main) {
            mIvFaceStatus?.setImageResource(if(faceResult.faceStatus)R.drawable.icon_face_success else R.drawable.icon_face_fail)
            mIvLuminance?.setImageResource(if(faceResult.luminance)R.drawable.icon_face_success else R.drawable.icon_face_fail)
            mIvBlurry?.setImageResource(if(faceResult.blurry)R.drawable.icon_face_success else R.drawable.icon_face_fail)
        }
    }

    private fun saveToImage(byteArray: ByteArray):File{
        val bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.size).let {
            Bitmap.createBitmap(it,0,0,it.width,it.height,
                Matrix().apply {
                    if(CameraUtils.getCameraID() == Camera.CameraInfo.CAMERA_FACING_FRONT){
                        //postRotate(-90f)
                        //postScale(-1f,1f)
                    }else{
                        //postRotate(90f)
                    }
                },

                true)
        }

        /*val parent = File(externalCacheDir,"/face/temp")
        if(!parent.exists()){
            parent.mkdirs()
        }
        val file = File(parent, "/${System.currentTimeMillis()}.png")*/
        val file = getImageFilePath()
        val out = FileOutputStream(file)
        bitmap.compress(Bitmap.CompressFormat.PNG,100,out)
        return file
    }

    private fun getImageFilePath():File{
        val parent = File(externalCacheDir,"/face/temp")
        if(!parent.exists()){
            parent.mkdirs()
        }
        val file = File(parent, "/${System.currentTimeMillis()}.png")
        return file
    }

    private  fun rotateNV21Clockwise(bytes:ByteArray):ByteArray{
        return if(CameraUtils.getCameraID() == Camera.CameraInfo.CAMERA_FACING_FRONT)
            ImageUtils.mirrorNV21(ImageUtils.rotateNV21CounterClockwise90(bytes,CameraUtils.DEFAULT_WIDTH,CameraUtils.DEFAULT_HEIGHT),CameraUtils.DEFAULT_HEIGHT,CameraUtils.DEFAULT_WIDTH)
        else ImageUtils.rotateNV21Clockwise90(bytes,CameraUtils.DEFAULT_WIDTH,CameraUtils.DEFAULT_HEIGHT)
    }

    private suspend fun innerFaceDetect(){
        //var startTime = System.currentTimeMillis()
        var bytes = getNextFrameData()
        bytes = rotateNV21Clockwise(bytes)
        val yuvimage =
            YuvImage(bytes, ImageFormat.NV21, CameraUtils.DEFAULT_HEIGHT,CameraUtils.DEFAULT_WIDTH, null)
        //val yuvBytes = yuvimage.yuvData
        val bos = ByteArrayOutputStream()
        yuvimage.compressToJpeg(Rect(0, 0, CameraUtils.DEFAULT_HEIGHT,CameraUtils.DEFAULT_WIDTH), 100, bos)
        val jdata: ByteArray = bos.toByteArray()
        //var startParseTime = System.currentTimeMillis()
        val faceResult = FaceHandler.faceRecognition(jdata,mMiniThresh,mMaxThresh,mBlueThresh,mIouNormal, mIouInter,mTLXRatio,mTLYRatio,mRBXRatio,mRBYRatio)
        //println("----------faceResult:$faceResult")
        updateFaceDetectToView(faceResult)
        if(faceResult.luminance && faceResult.blurry && faceResult.faceStatus){
            detectFaceSuccess(jdata, faceResult)
        }
        //var endTime = System.currentTimeMillis()
        //Log.e("Face","------------computerBlurry2 result:$faceResult,${endTime - startTime}ms,${endTime - startParseTime}ms,${System.currentTimeMillis()},${jdata.size}")
        if(mCoroutineScope.isActive){
            innerFaceDetect()
        }
    }

    private fun detectFaceSuccess(byteArray: ByteArray,faceResult:FaceResult){
        setResult(RESULT_OK, Intent().apply {
            if(mType == 0){
                val file = saveToImage(byteArray)
                if(isCompress){
                    val compressPath = getImageFilePath().absolutePath
                    val compressSuccess = CompressUtil.compressImage(file.absolutePath,compressPath,1280,720,70)
                    if(compressSuccess)
                        putExtra("result_compress_path",compressPath)
                }
                putExtra("result_path",file.absolutePath)

            }else{
                putExtra("result_bytes",byteArray)
            }
            if(faceResult.faceRect != null){
                putExtra("faceRect",faceResult.faceRect)
            }
            if(faceResult.detRect != null){
                putExtra("detRect",faceResult.detRect)
            }
            if(faceResult.log != null){
                putExtra("log",faceResult.log)
            }
        })
        mCoroutineScope.cancel()
        finish()
    }

    suspend fun getNextFrameData() = suspendCoroutine<ByteArray> {
        continuation ->
        CameraUtils.requestNextFrameData(object :NextPreviewFrame{
            override fun onFrame(bytes: ByteArray) {
                continuation.resume(bytes)
            }
        })
    }

    private fun showImage(){
        window.decorView.postDelayed({
            CameraUtils.requestNextFrameData { bytes->
                val yuvimage =
                    YuvImage(bytes, ImageFormat.NV21, CameraUtils.DEFAULT_WIDTH,CameraUtils.DEFAULT_HEIGHT, null)
                val bos = ByteArrayOutputStream()
                yuvimage.compressToJpeg(Rect(0, 0, CameraUtils.DEFAULT_WIDTH,CameraUtils.DEFAULT_HEIGHT), 100, bos)
                val jdata: ByteArray = bos.toByteArray()
                val bitmap = BitmapFactory.decodeByteArray(jdata, 0, jdata.size).let {
                    Bitmap.createBitmap(it,0,0,it.width,it.height,
                        Matrix().apply {
                            if(CameraUtils.getCameraID() == Camera.CameraInfo.CAMERA_FACING_FRONT){
                                postRotate(-90f)
                                postScale(-1f,1f)
                            }else{
                                postRotate(90f)
                            }
                        },

                        true)
                }
                mPreview?.setImageBitmap(bitmap)
                showImage()
            }
        },1000)
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String?>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            REQUEST_CAMERA -> if (grantResults.size > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED
            ) {
                mCameraRequested = true
                initView()
            }else{
                finish()
            }
        }
    }

    override fun onResume() {
        super.onResume()
        if (mCameraRequested) {
            CameraUtils.startPreview()
        }
        window.decorView.keepScreenOn = true
    }

    override fun onPause() {
        super.onPause()
        CameraUtils.stopPreview()
        window.decorView.keepScreenOn = false
    }

    override fun onDestroy() {
        super.onDestroy()
        mCoroutineScope.cancel()
    }

    override fun onClick(v: View) {
        when (v.getId()) {
            R.id.btn_take -> takePicture()
            R.id.btn_switch -> switchCamera()
            R.id.switch_camera -> switchCamera()
            R.id.close -> finish()
        }
    }

    /**
     * 拍照
     */
    private fun takePicture() {
        CameraUtils.takePicture(object : Camera.ShutterCallback {
            override fun onShutter() {

            }

        }, null, object : Camera.PictureCallback {
            override fun onPictureTaken(data: ByteArray, camera: Camera?) {
                CameraUtils.startPreview()
                var bitmap = BitmapFactory.decodeByteArray(data, 0, data.size)
                if (bitmap != null) {
                    bitmap = ImageUtils.getRotatedBitmap(bitmap, mOrientation)
                    val path =
                        (externalCacheDir?.absolutePath + "/Camera/"
                                + System.currentTimeMillis() + ".jpg")
                    try {
                        val fout = FileOutputStream(path)
                        val bos = BufferedOutputStream(fout)
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos)
                        bos.flush()
                        bos.close()
                        fout.close()
                    } catch (e: IOException) {
                        e.printStackTrace()
                    }
                }
                CameraUtils.startPreview()
            }
        })
    }


    /**
     * 切换相机
     */
    private fun switchCamera() {
        if (mCameraSurfaceView != null) {
            CameraUtils.switchCamera(1 - CameraUtils.getCameraID(), mCameraSurfaceView!!.holder)
            // 切换相机后需要重新计算旋转角度
            mOrientation =
                CameraUtils.calculateCameraPreviewOrientation(this)
        }
    }

}